<template>
  <div class="scrollbar" ref="root">
    <div
      :class="[props.wrapClass, 'scrollbar__wrap', gutter ? '' : 'scrollbar__wrap--hidden-default']"
      :style="computedWrapStyle"
      @scroll="handleScroll"
      ref="wrap"
    >
      <component
        :is="props.tag"
        :class="['scrollbar__view', props.viewClass]"
        :style="props.viewStyle"
        ref="resize"
      >
        <slot></slot>
      </component>
    </div>

    <Bar v-if="!props.native" :prop="{ move: moveX, size: sizeWidth }" />
    <Bar v-if="!props.native" :prop="{ vertical: true, move: moveY, size: sizeHeight }" />
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick, computed } from 'vue'
import _ from 'lodash'
import { addResizeListener, removeResizeListener } from '../../tools/resize-event'
import scrollbarWidth from '../../tools/scrollbar-width'
import { toObject } from '../../tools/util'
import Bar from './bar.vue'

// 定义组件名称
defineOptions({
  name: 'cg-scrollbar'
})

interface Props {
  native?: boolean
  wrapStyle?: any
  wrapClass?: any
  viewClass?: any
  viewStyle?: any
  wrapHeight: string
  innerScroll?: boolean
  scrollStep?: string
  noresize?: boolean
  tag: string
}

// 定义props
const props = withDefaults(defineProps<Props>(), { native: false, tag: 'div', noresize: false })

// 定义emits
const emit = defineEmits<{
  (e: 'handleScroll', data: { scrollTop: number; scrollLeft: number }): void
}>()

// 组件引用
const wrap = ref<HTMLDivElement | null>(null)
const resize = ref<HTMLElement | null>(null)

// 响应式数据
const sizeWidth = ref('0')
const sizeHeight = ref('0')
const moveX = ref(0)
const moveY = ref(0)
const gutter = ref(scrollbarWidth())
let update: ReturnType<typeof _.debounce> | undefined
let handleScrollEnd: ReturnType<typeof _.debounce> | undefined
const handleScrollEndStart = ref(false)

// 计算包装容器样式
const computedWrapStyle = computed(() => {
  let style = props.wrapStyle

  if (gutter.value) {
    const gutterWith = `-${gutter.value}px`
    const gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`
    let maxHeight: string | undefined = undefined
    let innerScrollWidth: string | undefined = undefined
    if (wrap.value?.parentElement instanceof Element) {
      const parent = wrap.value.parentElement
      if (props.wrapHeight === 'max') {
        maxHeight = Math.floor(parent.offsetHeight + gutter.value) + 'px'
      }
      if (props.innerScroll) {
        innerScrollWidth = gutter.value + 'px'
      }
    }

    if (Array.isArray(props.wrapStyle)) {
      style = toObject(props.wrapStyle)
      style.marginRight = style.marginBottom = gutterWith
      if (maxHeight) {
        style.height = maxHeight
        style.paddingBottom = '1px'
      }
      if (innerScrollWidth) {
        style.paddingRight = innerScrollWidth
      }
    } else if (typeof props.wrapStyle === 'string') {
      style += gutterStyle
      if (maxHeight) {
        style += `height: ${maxHeight};`
        style += `padding-bottom: 1px;`
      }
      if (innerScrollWidth) {
        style += `padding-right: ${innerScrollWidth};`
      }
    } else {
      style = gutterStyle
      if (maxHeight) {
        style += `height: ${maxHeight};`
        style += `padding-bottom: 1px;`
      }
      if (innerScrollWidth) {
        style += `padding-right: ${innerScrollWidth};`
      }
    }
  }

  return style
})

// 方法
const handleScroll = () => {
  if (wrap.value instanceof Element) {
    const { scrollTop, scrollLeft, clientHeight, clientWidth } = wrap.value
    moveY.value = (scrollTop * 100) / clientHeight
    moveX.value = (scrollLeft * 100) / clientWidth

    emit('handleScroll', {
      scrollTop,
      scrollLeft
    })

    // if (props.scrollStep && handleScrollEnd) {
    //   if (handleScrollEndStart.value) {
    //     return
    //   }
    //   handleScrollEndStart.value = true
    //   handleScrollEnd()
    // }
  }
}

// 生命周期
onMounted(() => {
  if (props.native) return

  update = _.debounce(() => {
    if (!wrap.value) return

    const heightPercentage = (wrap.value.clientHeight * 100) / wrap.value.scrollHeight
    const widthPercentage = (wrap.value.clientWidth * 100) / wrap.value.scrollWidth

    sizeHeight.value = heightPercentage < 100 ? heightPercentage + '%' : ''
    sizeWidth.value = widthPercentage < 100 ? widthPercentage + '%' : ''
  }, 0)

  nextTick(() => {
    update?.()
  })
  if (!props.noresize && resize.value && update) {
    addResizeListener(resize.value, update)
  }

  handleScrollEnd = _.debounce(
    () => {
      // if (wrap.value instanceof Element) {
      //   const wrapRect = wrap.value.getBoundingClientRect()
      //   let nodeList = wrap.value.querySelectorAll(props.scrollStep)
      //   const { scrollTop } = wrap.value
      //   let lastTop = 0
      //   for (const node of nodeList) {
      //     if (node instanceof Element) {
      //       const rect = node.getBoundingClientRect()
      //       let top = wrapRect.top - rect.top
      //       if (top < 0) {
      //         let minDetail = top
      //         if (Math.abs(lastTop) < Math.abs(top)) {
      //           minDetail = lastTop
      //         }
      //         wrap.value.scrollTo({
      //           top: scrollTop - minDetail,
      //           behavior: 'smooth'
      //         })
      //         setTimeout(() => {
      //           handleScrollEndStart.value = false
      //         }, 200)
      //         return
      //       }
      //       lastTop = top
      //     }
      //   }
      // }
    },
    500,
    { trailing: true }
  )
})

defineExpose({
  update() {
    update?.()
  }
})

onBeforeUnmount(() => {
  if (props.native) return
  if (!props.noresize && resize.value && update) {
    removeResizeListener(resize.value, update)
  }
})
</script>

<style lang="scss">
.scrollbar {
  overflow: hidden;
  position: relative;

  &:hover,
  &:active,
  &:focus {
    > .scrollbar__bar {
      opacity: 1;
      transition: opacity 340ms ease-out;
    }
  }

  .scrollbar__wrap {
    position: relative;
    overflow: scroll;
    height: 100%;
  }

  .scrollbar__wrap--hidden-default {
    scrollbar-width: none;

    &::-webkit-scrollbar {
      width: 0;
      height: 0;
    }
  }

  .scrollbar__thumb {
    position: relative;
    display: block;
    width: 0;
    height: 0;
    cursor: pointer;
    border-radius: inherit;
    background-color: rgba(51, 51, 51, 0.6);
    transition: 0.3s background-color;

    &:hover {
      background-color: rgba(51, 51, 51, 0.8);
    }
  }

  $bar-size: 6px;
  $bar-radius: 0px;
  $bar-opacity: 0.5;

  .scrollbar__bar {
    position: absolute;
    right: 1px;
    bottom: 1px;
    z-index: 1;
    border-radius: $bar-radius;
    opacity: $bar-opacity;
    transition: opacity 120ms ease-out;

    &.is-horizontal {
      height: $bar-size;
      left: 1px;

      > div {
        height: 100%;
      }
    }

    &.is-vertical {
      width: $bar-size;
      top: 1px;

      > div {
        width: 100%;
      }
    }
  }
}
</style>
